package ityc.Server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import ityc.connection.serverHandler.HttpServerHandler;
import ityc.connection.serverHandler.ServerHandler;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class Server {
    private int port;

    private int httpPort;

    private static Server server;

    private HashMap<String, Channel> channelHashMap = new HashMap<>();

    public Map<String, Channel> getChannelHashMap() {
        if (this.channelHashMap.isEmpty()) return new HashMap<>();
        return this.channelHashMap;
    }

    public Channel getHttpChannel() {
        return this.channelHashMap.get("http");
    }

    public Channel getClientChannel() {
        return this.channelHashMap.get("client");
    }

    public static Server getInstance() {
        if (server != null) {
            return server;
        } else {
            server = new Server(18848, 12138);
            return server;
        }
    }

    private Server(int port, int httpPort) {
        this.port = port;
        this.httpPort = httpPort;
    }

    private ServerBootstrap tcpBootstrap;
    private ServerBootstrap httpBootstrap;

    public void start() throws Exception {
        final ServerHandler serverHandler = new ServerHandler();
        EventLoopGroup tcpLoopGroup = new NioEventLoopGroup();
        EventLoopGroup httpLoopGroup = new NioEventLoopGroup();
        try {
            this.tcpBootstrap = new ServerBootstrap();
            this.tcpBootstrap.group(tcpLoopGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(this.port))
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 保持连接
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    .addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS)) //心跳检测
                                    .addLast(serverHandler);
                        }
                    });
            this.httpBootstrap = new ServerBootstrap();
            this.httpBootstrap.group(httpLoopGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(this.httpPort)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 请求解码器
                            socketChannel.pipeline().addLast("http-decoder", new HttpRequestDecoder());
                            // 将HTTP消息的多个部分合成一条完整的HTTP消息
                            socketChannel.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65535));
                            // 响应转码器
                            socketChannel.pipeline().addLast("http-encoder", new HttpResponseEncoder());
                            // 解决大码流的问题，ChunkedWriteHandler：向客户端发送HTML5文件
                            socketChannel.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                            // 自定义处理handler
                            socketChannel.pipeline().addLast("http-server", new HttpServerHandler());
                        }
                    });
            ChannelFuture tcpFuture = this.tcpBootstrap.bind().sync();
            ChannelFuture httpFuture = this.httpBootstrap.bind();
            Channel tcpChannel = tcpFuture.channel();
            this.channelHashMap.put("client", tcpChannel);
            this.channelHashMap.put("http", httpFuture.channel());
            tcpChannel.closeFuture().sync();

        } finally {
            httpLoopGroup.shutdownGracefully().sync();

        }
    }
}


